examples: web-standard twin of the bearer-auth story#2424
Conversation
|
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
Every auth example is Express-based, so the runtime-neutral requireBearerAuth had no runnable story. bearer-auth-web mirrors bearer-auth on the @modelcontextprotocol/server surface alone: host and origin validation plus the bearer gate in front of createMcpHandler, composed as one fetch(request) handler that is the entire server on Cloudflare Workers, Deno, or Bun; toNodeHandler bridges it onto node:http for the example matrix. The client mirrors the sibling's assertions, parsing the 401 challenge with extractWWWAuthenticateParams.
d213465 to
ea2f452
Compare
There was a problem hiding this comment.
LGTM — example-only addition mirroring the existing bearer-auth story on the web-standard surface; all SDK exports it uses exist and the story is self-verifying in the CI example matrix.
Extended reasoning...
Overview
The PR adds a new self-contained example story, examples/bearer-auth-web/, plus a one-row addition to examples/README.md and the corresponding pnpm-lock workspace entry. No SDK package code is touched. The story is a near-verbatim twin of the existing examples/bearer-auth (same factory, verifier, tool, and client assertions), swapping the Express middleware for the runtime-neutral requireBearerAuth / host-and-origin guard exports from @modelcontextprotocol/server (added in #2420, already on main) composed as one fetch(request) handler and bridged with toNodeHandler.
Correctness verification
I confirmed every symbol the example imports exists on the packages it names: requireBearerAuth, hostHeaderValidationResponse, localhostAllowedHostnames, originValidationResponse, localhostAllowedOrigins, OAuthError/OAuthErrorCode, McpServerFactory (packages/server/src/index.ts), toNodeHandler (accepts any { fetch } shape via FetchLikeMcpHandler), and extractWWWAuthenticateParams (packages/client/src/index.ts). The gate's return contract (AuthInfo | Response) and handler.fetch(request, { authInfo }) pass-through both match the current implementations. The client mirrors the sibling's assertions, additionally exercising extractWWWAuthenticateParams on the 401 challenge.
Security risks
The example demonstrates a static demo token verifier, which is clearly commented as a placeholder for real JWT/introspection verification — the same pattern as the existing sibling story. No auth code in the SDK itself changes, so there is no production security surface here.
Level of scrutiny
Example-only, no changeset needed (private workspace packages), no public API or protocol behavior changes. The story is self-verifying and runs in the examples (build + e2e) CI gate over both eras, so any wiring mistake would fail CI. The bug hunting system found no issues, and there are no outstanding reviewer comments. This fits the low-risk, self-contained category suitable for shadow approval.
There was a problem hiding this comment.
LGTM — example-only PR that mirrors the existing bearer-auth story on the web-standard server surface; all referenced exports exist and the self-verifying client assertions match the SDK's requireBearerAuth behavior.
Extended reasoning...
Overview
This PR adds a new example story examples/bearer-auth-web/ (README, client.ts, server.ts, package.json), one row in examples/README.md, and the corresponding pnpm-lock.yaml importer entry. No SDK package source is touched. The story is a web-standard twin of the existing examples/bearer-auth Express story: the server composes hostHeaderValidationResponse / originValidationResponse, the runtime-neutral requireBearerAuth, and createMcpHandler into a single fetch(request) handler, bridged onto node:http via toNodeHandler for the CI matrix.
Correctness
I verified that every symbol the example imports is exported today from packages/server/src/index.ts (requireBearerAuth, hostHeaderValidationResponse, localhostAllowedHostnames, localhostAllowedOrigins, originValidationResponse, etc.) and packages/client (extractWWWAuthenticateParams), so the stacked-base dependency has already landed. The client's assertions match the implementation: a missing Authorization header maps to OAuthErrorCode.InvalidToken, and bearerAuthChallengeResponse returns 401 with error="invalid_token" in the WWW-Authenticate challenge, which is exactly what the client asserts. The verifier, factory, and authenticated-call assertions are copied verbatim from the sibling story, which passes in CI unchanged.
Security risks
None beyond the example itself: the static demo-token verifier is clearly documented as a placeholder, and the example actually demonstrates good practice (host/origin validation plus bearer gating in front of the MCP handler). No auth or crypto code in the SDK packages is modified.
Level of scrutiny
Low — this is example/documentation-only content in a private, non-published workspace package. The example is self-verifying and runs in the examples (build + e2e) CI job over both protocol eras, so any wiring mistake would fail the build rather than ship silently. No changeset is needed since no publishable package changes.
Other factors
The bug hunting system found no issues, and the diff follows the repo's established example conventions (per-story workspace package, example config block, README table entry). The PR description's claims match the diff.
Reopened: #2423 was accidentally merged into its stacked base branch (the merge button on a stacked PR targets the base, not main); the base has been reset and this resubmits the identical content. Stacked on #2420, as its runnable proof.
Motivation and Context
Every auth example is Express-based, so the runtime-neutral
requireBearerAuthhad no story exercising it end to end.examples/bearer-auth-webis the web-standard twin ofexamples/bearer-auth, mirroring it on the@modelcontextprotocol/serversurface alone: host/origin validation plusrequireBearerAuthgatingcreateMcpHandler, composed as onefetch(request)handler. On Cloudflare Workers, Deno, or Bun that handler is the whole server;toNodeHandlerbridges it ontonode:http(one line) so the story runs in the example matrix. The factory and verifier are copied verbatim from the sibling so a reader diffing the two sees exactly what changes between runtimes.Deliberately matching the sibling's scope: no Authorization Server and no discovery documents. The Express auth path keeps its end-to-end coverage in the
oauthandoauth-client-credentialsstories; a web-standard twin of the fulloauthstory can follow once the discovery serving in #2422 lands.How Has This Been Tested?
httptransport,era: dual, same config as the sibling).toNodeHandler's fallback).bearer-authstory passes unchanged.Breaking Changes
None; example-only.
Types of changes
Checklist
Additional context
Merge order for the stack: #2420 first (into main), then the stacked PRs retarget automatically.